/**
*	@class Vector2
*
*	@brief This class implements a 2D vector object.
*
*	@author William McVicar
*/

#pragma once

#ifndef __VECTOR2_H_INCLUDED__
#define __VECTOR2_H_INCLUDED__

#pragma warning(disable : 4201) 

namespace Protein
{

template< class Real >
class Vector2
{

public:

	Vector2();
	Vector2( Real x, Real y );
	Vector2( Real* values );
	Vector2( const Vector2& other );
	Vector2& operator=( const Vector2& rhs );
	~Vector2();

	void                 set( Real x, Real y );
  void                 set( Real val );

	Vector2&             operator+=( const Vector2& rhs );
	Vector2&             operator-=( const Vector2& rhs );
	Vector2&             operator*=( const Real rhs );
	Vector2&             operator/=( const Real rhs );

	Vector2              operator+( const Vector2& rhs ) const;
	Vector2              operator-( const Vector2& rhs ) const;
	Vector2              operator*( const Real rhs ) const;
	Vector2              operator/( const Real rhs ) const;
	
	bool                 operator==( const Vector2& rhs ) const;
	bool                 operator!=( const Vector2& rhs ) const;
  bool                 operator< ( const Vector2& rhs ) const;
  bool                 operator<=( const Vector2& rhs ) const;
  bool                 operator> ( const Vector2& rhs ) const;
  bool                 operator>=( const Vector2& rhs ) const;

	Real                 length() const;
	Real                 lengthSqr() const;

	void                 normalise();
	Vector2              normalised() const;

	Real                 distance( const Vector2& other );
  Real                 distanceSqr( const Vector2& other );

  Real                 dot( const Vector2& rhs ) const;
  Vector2              perp() const;
  void                 toPerp();

  Real                 dotPerp( const Vector2& v ) const;
  Vector2              unitPerp() const;

  static Real          distance( const Vector2& start, const Vector2& dest );
	static Real          distanceSqr( const Vector2& start, const Vector2& dest );

	static Real          dot( const Vector2& a, const Vector2& b );
  static Real          dotPerp( const Vector2& a, const Vector2& b );

	union
	{
		struct
    { 
      Real x;
      Real y; 
    };

	  Real m_components[3];
  };

	static const Vector2 Zero;	
	static const Vector2 One;	  
	static const Vector2 i;		  
	static const Vector2 j;		  
	static const Vector2 negi;	
	static const Vector2 negj;

};

template< class Real >
Vector2<Real>::Vector2()
{
  memset( m_components, 0, sizeof( Real ) * 2 );
}

template< class Real >
Vector2<Real>::Vector2( Real _x, Real _y )
: x( _x )
, y( _y )
{
}

template< class Real >
Vector2<Real>::Vector2( Real* values )
{
  memcpy( m_components, values, sizeof( Real ) * 2 );
}

template< class Real >
Vector2<Real>::Vector2( const Vector2& other )
: x( other.x )
, y( other.y )
{
}

template< class Real >
Vector2<Real>& Vector2<Real>::operator=( const Vector2<Real>& rhs )
{
  memcpy( m_components, rhs.m_components, sizeof( Real ) * 2 );
  return *this;
}

template< class Real >
Vector2<Real>::~Vector2()
{
}

template< class Real >
void Vector2<Real>::set( Real _x, Real _y )
{
	x = _x;
	y = _y;
}

template< class Real >
void Vector2<Real>::set( Real val )
{
	memset( m_components, val, sizeof( Real ) * 2 );
}

template< class Real > 
Vector2<Real>& Vector2<Real>::operator+=( const Vector2& rhs )
{
	x += rhs.x;
	y += rhs.y;

	return *this;
}

template< class Real >
Vector2<Real>& Vector2<Real>::operator-=( const Vector2& rhs )
{
	x -= rhs.x;
	y -= rhs.y;

	return *this;	
}

template< class Real >
Vector2<Real>& Vector2<Real>::operator*=( Real rhs )
{
	x *= rhs;
	y *= rhs;

	return *this;	
}

template< class Real >
Vector2<Real>& Vector2<Real>::operator/=( Real rhs )
{
	x /= rhs;
	y /= rhs;

	return *this;	
}

template< class Real >
Vector2<Real> Vector2<Real>::operator+( const Vector2& rhs ) const
{
	Vector2 temp;

	temp.x = x + rhs.x;
	temp.y = y + rhs.y;

	return temp;
}

template< class Real >
Vector2<Real> Vector2<Real>::operator-( const Vector2& rhs ) const
{
	Vector2 temp;

	temp.x = x - rhs.x;
	temp.y = y - rhs.y;

	return temp;	
}

template< class Real >
Vector2<Real> Vector2<Real>::operator*( const Real rhs ) const
{
	Vector2 temp;

	temp.x = x * rhs;
	temp.y = y * rhs;

	return temp;
}

template< class Real >
Vector2<Real> Vector2<Real>::operator/( const Real rhs ) const
{
	Vector2 temp;

	temp.x = x / rhs;
	temp.y = y / rhs;

	return temp;	
}

template< class Real > 
bool Vector2<Real>::operator !=(const Vector2<Real>& rhs) const
{
	return( !Math<Real>::AreEqual( x, rhs.x ) && !Math<Real>::AreEqual( y, rhs.y ) );
}

template< class Real > 
bool Vector2<Real>::operator ==(const Vector2<Real>& rhs) const
{
	return( Math<Real>::AreEqual( x, rhs.x ) && Math<Real>::AreEqual( y, rhs.y ) );
}

template< class Real > 
bool Vector2<Real>::operator >(const Vector2<Real>& rhs) const
{
	if( Math<Real>::AreEqual( x, rhs.x ) && Math<Real>::AreEqual( y, rhs.y ) )
		return false;

	return( x > rhs.x && y > rhs.y );
}

template< class Real > 
bool Vector2<Real>::operator >=(const Vector2<Real>& rhs) const
{
	if( Math<Real>::AreEqual( x, rhs.x ) && Math<Real>::AreEqual( y, rhs.y ) )
		return true;

	return( x > rhs.x && y > rhs.y );
}

template< class Real > 
bool Vector2<Real>::operator <=(const Vector2<Real>& rhs) const
{
	if( Math<Real>::AreEqual( x, rhs.x ) && Math<Real>::AreEqual( y, rhs.y ) ) 
		return true;

	return( x < rhs.x && y < rhs.y );
}

template< class Real > 
bool Vector2<Real>::operator <(const Vector2<Real>& rhs) const
{
	if( Math<Real>::AreEqual( x, rhs.x ) && Math<Real>::AreEqual( y, rhs.y ) ) 
		return false;

	return( x < rhs.x && y < rhs.y );
}

template<class Real> 
Real Vector2<Real>::length() const
{
	return( Math<Real>::Sqrt( ( x * y ) +( y * y ) ) );
}

template<class Real> 
Real Vector2<Real>::lengthSqr() const
{
	return ( ( x * y ) +( y * y ) );
}

template<class Real>
void Vector2<Real>::normalise()
{
	Real fMag = Math<Real>::Sqrt(( x * y ) +( y * y ) );

	if( fMag > Math<Real>::ZERO_TOLERANCE )
	{
		Real fInvLength = ((Real)1.0/fMag);
		x *= fInvLength;
		y *= fInvLength;
	}
	else
	{
		Real fInvLength = ((Real)0.0);
		x *= fInvLength;
		y *= fInvLength;
	}
}

template<class Real>
Vector2<Real> Vector2<Real>::normalised() const
{
	Vector2<Real> temp;

	Real fMag = Math<Real>::Sqrt(( x * y ) +( y * y ) );

	if( fMag > Math<Real>::ZERO_TOLERANCE )
	{
		Real fInvLength = ((Real)1.0/fMag);
		temp.x = x * fInvLength;
		temp.y = y * fInvLength;
	}
	else
	{
		Real fInvLength = ((Real)0.0);
		temp.x = x * fInvLength;
		temp.y = y * fInvLength;
	}

	return temp;
}

template< class Real > 
Real Vector2<Real>::distance(const Vector2<Real>& other)
{
	Real fDeltaX = other.x - x;
	Real fDeltaY = other.y - y;

	return( Math::Sqrt<Real>(( fDeltaX * fDeltaX ) +( fDeltaY * fDeltaY ) ) );
}

template< class Real > 
Real Vector2<Real>::distanceSqr(const Vector2<Real>& other)
{
	Real fDeltaX = other.x - x;
	Real fDeltaY = other.y - y;

	return( ( fDeltaX * fDeltaX ) +( fDeltaY * fDeltaY ) );
}

template< class Real > 
Real Vector2<Real>::distance(const Vector2<Real>& start, const Vector2<Real>& dest)
{
	Real fDeltaX = dest.x - start.x;
	Real fDeltaY = dest.y - start.y;

	return( Math::Sqrt<Real>(( fDeltaX * fDeltaX ) +( fDeltaY * fDeltaY ) ) );
}

template< class Real > 
Real Vector2<Real>::distanceSqr(const Vector2<Real>& start, const Vector2<Real>& dest)
{
	Real fDeltaX = dest.x - start.x;
	Real fDeltaY = dest.y - start.y;

	return( ( fDeltaX * fDeltaX ) +( fDeltaY * fDeltaY ) );
}

template< class Real > 
Real Vector2<Real>::dot(const Vector2<Real>& a, const Vector2<Real>& b)
{
	return ( ( a.x * b.x ) +( a.y * b.y ) );
}

template< class Real > 
Real Vector2<Real>::dot(const Vector2<Real>& rhs ) const
{
	return ( ( x * rhs.x ) +( y * rhs.y ) );
}

template <class Real>
Vector2<Real> Vector2<Real>::perp() const
{
	return Vector2<Real>( y, Math<Real>::FlipSign(x) );
}

template <class Real>
void Vector2<Real>::toPerp()
{
  Real temp = x;

  x = y;
  y = Math<Real>::FlipSign(temp);
}

template <class Real>
Vector2<Real> Vector2<Real>::unitPerp() const
{
  return perp().normalise();
}

template <class Real>
Real Vector2<Real>::dotPerp( const Vector2<Real>& v ) const
{
  return ( ( x * v.y ) - ( y * v.x ) );
}

template <class Real>
Real Vector2<Real>::dotPerp( const Vector2<Real>& a, const Vector2<Real>& b )
{
  return ( a.dot( b.perp() ) );
}

typedef Vector2<float>  Vector2f;
typedef Vector2<double> Vector2d;

}

#pragma warning(default : 4201) 

#endif // __VECTOR2_H_INCLUDED__
